﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Compiler
{
    public class FirstSets
    {
        private Grammar grammar;
        private Dictionary<Symbol, HashSet<Symbol>> firstSets;

        internal FirstSets(Grammar grammar, Dictionary<Symbol, HashSet<Symbol>> firstSets)
        {
            this.firstSets = firstSets;
            this.grammar = grammar;
        }

        public ISet<Symbol> this[Symbol symbol]
        {
            get
            {
                return firstSets[symbol];
            }
        }
        
        public ISet<Symbol> ForSetOfChains(HashSet<List<Symbol>> Chainset)
        {
            var final = new HashSet<Symbol>(new Symbol.SymbolEqualityComparer());
            foreach (var chain in Chainset)
                final.UnionWith(ForChain(chain));
            return final;
        }

        public ISet<Symbol> ForChain(List<Symbol> Chain)
        {
            var EpsilonSet = new HashSet<Symbol>();
            EpsilonSet.Add(Symbol.Epsilon);

            var Terminals = grammar.getTerminals();
            var NonTerminals = grammar.getNonterminals();

            if (Chain.Exists(symbol =>
                (!(symbol is Nonterminal) || !NonTerminals.Contains(symbol as Nonterminal))
                &&
                (!(symbol is Terminal) || !Terminals.Contains(symbol as Terminal))
                && symbol != Symbol.Epsilon
                && symbol != Symbol.EOF //vorsicht, ist das richtig?
                ))
                throw new Exception("Unknown Symbol in Chain");

            var set = new HashSet<Symbol>();

            for (int i = 0; i < Chain.Count; ++i)
            {
                Symbol symbol = Chain[i];
                if (symbol == Symbol.Epsilon)
                    continue;

                set.UnionWith(firstSets[symbol].Except(EpsilonSet));

                if (!firstSets[symbol].Contains(Symbol.Epsilon))
                    break;
                else if (i == Chain.Count - 1)
                    set.UnionWith(EpsilonSet);
            }

            return set;
        }
    }
}
